//
// detail/socket_option.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef ASIO_DETAIL_SOCKET_OPTION_HPP
#define ASIO_DETAIL_SOCKET_OPTION_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include <cstddef>
#include <stdexcept>
#include "asio/detail/socket_types.hpp"
#include "asio/detail/throw_exception.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {
    namespace detail {
        namespace socket_option {

// Helper template for implementing boolean-based options.
            template<int Level, int Name>
            class boolean {
            public:
                // Default constructor.
                boolean()
                        : value_(0) {
                }

                // Construct with a specific option value.
                explicit boolean(bool v)
                        : value_(v ? 1 : 0) {
                }

                // Set the current value of the boolean.
                boolean &operator=(bool v) {
                    value_ = v ? 1 : 0;
                    return *this;
                }

                // Get the current value of the boolean.
                bool value() const {
                    return !!value_;
                }

                // Convert to bool.
                operator bool() const {
                    return !!value_;
                }

                // Test for false.
                bool operator!() const {
                    return !value_;
                }

                // Get the level of the socket option.
                template<typename Protocol>
                int level(const Protocol &) const {
                    return Level;
                }

                // Get the name of the socket option.
                template<typename Protocol>
                int name(const Protocol &) const {
                    return Name;
                }

                // Get the address of the boolean data.
                template<typename Protocol>
                int *data(const Protocol &) {
                    return &value_;
                }

                // Get the address of the boolean data.
                template<typename Protocol>
                const int *data(const Protocol &) const {
                    return &value_;
                }

                // Get the size of the boolean data.
                template<typename Protocol>
                std::size_t size(const Protocol &) const {
                    return sizeof(value_);
                }

                // Set the size of the boolean data.
                template<typename Protocol>
                void resize(const Protocol &, std::size_t s) {
                    // On some platforms (e.g. Windows Vista), the getsockopt function will
                    // return the size of a boolean socket option as one byte, even though a
                    // four byte integer was passed in.
                    switch (s) {
                        case sizeof(char):
                            value_ = *reinterpret_cast<char *>(&value_) ? 1 : 0;
                            break;
                        case sizeof(value_):
                            break;
                        default: {
                            std::length_error ex("boolean socket option resize");
                            asio::detail::throw_exception(ex);
                        }
                    }
                }

            private:
                int value_;
            };

// Helper template for implementing integer options.
            template<int Level, int Name>
            class integer {
            public:
                // Default constructor.
                integer()
                        : value_(0) {
                }

                // Construct with a specific option value.
                explicit integer(int v)
                        : value_(v) {
                }

                // Set the value of the int option.
                integer &operator=(int v) {
                    value_ = v;
                    return *this;
                }

                // Get the current value of the int option.
                int value() const {
                    return value_;
                }

                // Get the level of the socket option.
                template<typename Protocol>
                int level(const Protocol &) const {
                    return Level;
                }

                // Get the name of the socket option.
                template<typename Protocol>
                int name(const Protocol &) const {
                    return Name;
                }

                // Get the address of the int data.
                template<typename Protocol>
                int *data(const Protocol &) {
                    return &value_;
                }

                // Get the address of the int data.
                template<typename Protocol>
                const int *data(const Protocol &) const {
                    return &value_;
                }

                // Get the size of the int data.
                template<typename Protocol>
                std::size_t size(const Protocol &) const {
                    return sizeof(value_);
                }

                // Set the size of the int data.
                template<typename Protocol>
                void resize(const Protocol &, std::size_t s) {
                    if (s != sizeof(value_)) {
                        std::length_error ex("integer socket option resize");
                        asio::detail::throw_exception(ex);
                    }
                }

            private:
                int value_;
            };

// Helper template for implementing linger options.
            template<int Level, int Name>
            class linger {
            public:
                // Default constructor.
                linger() {
                    value_.l_onoff = 0;
                    value_.l_linger = 0;
                }

                // Construct with specific option values.
                linger(bool e, int t) {
                    enabled(e);
                    timeout ASIO_PREVENT_MACRO_SUBSTITUTION(t);
                }

                // Set the value for whether linger is enabled.
                void enabled(bool value) {
                    value_.l_onoff = value ? 1 : 0;
                }

                // Get the value for whether linger is enabled.
                bool enabled() const {
                    return value_.l_onoff != 0;
                }

                // Set the value for the linger timeout.
                void timeout

                ASIO_PREVENT_MACRO_SUBSTITUTION(int value) {
#if defined(WIN32)
                    value_.l_linger = static_cast<u_short>(value);
#else
                    value_.l_linger = value;
#endif
                }

                // Get the value for the linger timeout.
                int timeout

                ASIO_PREVENT_MACRO_SUBSTITUTION() const {
                    return static_cast<int>(value_.l_linger);
                }

                // Get the level of the socket option.
                template<typename Protocol>
                int level(const Protocol &) const {
                    return Level;
                }

                // Get the name of the socket option.
                template<typename Protocol>
                int name(const Protocol &) const {
                    return Name;
                }

                // Get the address of the linger data.
                template<typename Protocol>
                detail::linger_type *data(const Protocol &) {
                    return &value_;
                }

                // Get the address of the linger data.
                template<typename Protocol>
                const detail::linger_type *data(const Protocol &) const {
                    return &value_;
                }

                // Get the size of the linger data.
                template<typename Protocol>
                std::size_t size(const Protocol &) const {
                    return sizeof(value_);
                }

                // Set the size of the int data.
                template<typename Protocol>
                void resize(const Protocol &, std::size_t s) {
                    if (s != sizeof(value_)) {
                        std::length_error ex("linger socket option resize");
                        asio::detail::throw_exception(ex);
                    }
                }

            private:
                detail::linger_type value_;
            };

        } // namespace socket_option
    } // namespace detail
} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_DETAIL_SOCKET_OPTION_HPP
